package com.siyoumi.service;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.siyoumi.component.http.InputData;
import com.siyoumi.component.http.XHttpContext;
import com.siyoumi.entity.EntityBase;
import com.siyoumi.entity.SysAccount;
import com.siyoumi.exception.EnumSys;
import com.siyoumi.mapper.SysAppMapper;
import com.siyoumi.mybatispuls.JoinWrapperPlus;
import com.siyoumi.mybatispuls.MapperBase;
import com.siyoumi.util.XReturn;
import com.siyoumi.util.XSqlStr;
import com.siyoumi.util.XStr;
import com.siyoumi.validator.XValidator;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

public interface ServiceBase<T extends EntityBase<T>, M extends MapperBase<T>>
        extends IService<T> {
    M mapper();

    default LambdaQueryWrapper<T> ql() {
        return new QueryWrapper<T>().lambda();
    }

    default QueryWrapper<T> q() {
        return new QueryWrapper<T>();
    }

    default JoinWrapperPlus<T> join() {
        return new JoinWrapperPlus<T>();
    }

    default Page<T> page(long pageIndex, long pageSize) {
        return page(pageIndex, pageSize, true);
    }

    default Page<T> page(long pageIndex, long pageSize, boolean searchCount) {
        return new Page<T>(pageIndex, pageSize, 0L, searchCount);
    }


    /**
     * 例：
     * id = acc_id;
     * x_id = acc_x_id;
     *
     * @param key
     */
    String getEntityKeyName(String key);

    default String fdKey() {
        return getEntityKeyName("id");
    }

    default String fdX() {
        return getEntityKeyName("x_id");
    }

    default String fdAccId() {
        return getEntityKeyName("acc_id");
    }

    //后台数据添加搜索acc条件
    default void addQueryAcc(JoinWrapperPlus<T> query) {
        if (!"admin".equals(XHttpContext.getTokenDataType())) {
            //未后台接口，不添加条件
            return;
        }

        SysAccountService svcAcc = SysAccountService.getBean();
        SysAccount entityAcc = svcAcc.getSysAccountByToken();
        if (entityAcc == null) {
            XValidator.err(EnumSys.ERR_VAL.getR("后台请求，请检查acc_id"));
        }

        if (svcAcc.isSpuerAdminOrDev(entityAcc)) {
            //开发者、超管，pass
        } else {
            query.eq(fdAccId(), entityAcc.getKey());
        }
    }


    default T first(Object id) {
        JoinWrapperPlus<T> query = join();
        query.eq(fdKey(), id);
        return first(query);
    }

    default T first(QueryWrapper<T> query) {
        return first(query, false);
    }

    default T first(QueryWrapper<T> query, Boolean lock) {
        try {
            String last = XSqlStr.LIMIT_1;
            if (lock) {
                SysAppService.getBean().getBaseMapper().setInnodbLockWaitTimeout(3);
                last = XStr.concat(XSqlStr.LIMIT_1, " ", XSqlStr.FOR_UPDATE);
            }
            query.last(last);
            List<T> list = mapper().get(query);
            if (list.size() <= 0) {
                return null;
            }

            return list.get(0);
        } catch (Exception ex) {
            throw ex;
        } finally {
            if (lock) {
                SysAppService.getBean().getBaseMapper().setInnodbLockWaitTimeout(10);
            }
        }
    }

    default List<T> get(List<String> ids) {
        return get(ids, null);
    }

    default List<T> get(List<String> ids, String x) {
        QueryWrapper<T> query = q();
        query.in(fdKey(), ids);

        if (XStr.hasAnyText(x)) {
            query.eq(fdX(), x);
        }

        return get(query);
    }

    default List<T> get(Wrapper<T> query) {
        return mapper().get(query);
    }

    default IPage<T> get(IPage<T> page, Wrapper<T> query) {
        return mapper().get(page, query);
    }

    default Map<String, Object> firstMap(JoinWrapperPlus<T> query) {
        query.last(XSqlStr.LIMIT_1);
        return mapper().firstMap(query);
    }

    default List<Map<String, Object>> getMaps(Wrapper<T> query) {
        return mapper().getMaps(query);
    }

    default IPage<Map<String, Object>> getMaps(IPage<T> page, Wrapper<T> query) {
        return mapper().getMaps(page, query);
    }

    T getEntityLock(String id);

    default T getEntity(String id, Boolean getDb) {
        if (getDb) {
            String key = getEntityCacheKey(id, false);
            XHttpContext.del(key);
        }
        return getEntity(id);
    }

    T getEntity(String id);

    T getEntity(String id, String x);

    T loadEntity(String id);

    T loadEntity(String id, String x);

    T loadEntity(Map<String, Object> map);

    /**
     * 获取redis key，带实体属性
     * <p>
     * com.siyoumi.entity.SysAccount
     * 变
     * cache_model:SysAccount|wp|test
     *
     * @param keyFix 后缀
     * @return redis key
     */
    String getEntityCacheKey(String keyFix);

    String getEntityCacheKey(String keyFix, Boolean addX);

    /**
     * 删除redis
     *
     * @param keyFix 后缀
     */
    void delEntityCache(String keyFix);


    /**
     * 保存实体
     *
     * @param inputData   操作参数；
     * @param autoSetId   true：自动设置ID
     * @param ignoreField 排队字段
     * @return 结果
     */
    XReturn saveEntity(InputData inputData, Boolean autoSetId, List<String> ignoreField);

    XReturn saveEntity(InputData inputData, Object editEntity, Boolean autoSetId, List<String> ignoreField);

    XReturn saveEntityBefore(InputData inputData, T entity);

    XReturn saveEntityAfter(InputData inputData, T entity);


    /**
     * sum
     *
     * @param query  查询
     * @param sumSql sum字段，例：funrec_num_real、funrec_num * funrec_ab_type
     */
    BigDecimal sum(JoinWrapperPlus<T> query, String sumSql);

    Long count(JoinWrapperPlus<T> query);

    /**
     * 添加或者更新，会过滤值相同的字段
     * 字段值为null的数据不会更新 mybatis-plus 机制
     */
    default T saveOrUpdatePassEqualField(T entitySrc, T entitySaveOrUpdate) {
        return saveOrUpdatePassEqualField(entitySrc, entitySaveOrUpdate, null);
    }

    T saveOrUpdatePassEqualField(T entitySrc, T entitySaveOrUpdate, List<String> ignoreField);
}
